package com.snake.excel2java.excel.generator.file;

import java.io.File;
import java.io.FileOutputStream;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;

@Slf4j
@Component
@NoArgsConstructor
public class Excel2007Creator {


	public void generate() {
		
		File dir = new File("");
		
		String path = dir.getAbsoluteFile().getParentFile() + "/excel2java/";

		String excelDirPath = dir.getAbsoluteFile().getParentFile() + "/excels/";
				
		File exceldir = new File(excelDirPath);
		
		if(!exceldir.isDirectory())
			exceldir.mkdirs();
		
		log.info("generate java file from dir: {}", excelDirPath);
		
		String excel2JavaPath = path + "\\src\\main\\java\\com\\snake\\excel2java\\excel\\common\\file\\";
		String excel2JavaExtPath = path + "\\src\\main\\java\\com\\snake\\excel2java\\excel\\common\\ext\\file\\";
		
		String excel2JavaRowPath = path + "\\src\\main\\java\\com\\snake\\excel2java\\excel\\common\\row\\";
		String excel2JavaRowExtPath = path + "\\src\\main\\java\\com\\snake\\excel2java\\excel\\common\\ext\\row\\";
		
		File excel2JavaPathDir = new File(excel2JavaPath);
		
		if(!excel2JavaPathDir.isDirectory())
			excel2JavaPathDir.mkdirs();
		
		for(File file: excel2JavaPathDir.listFiles()) 
			if(file.isFile())
				file.delete();
		
		File excel2JavaRowPathDir = new File(excel2JavaRowPath);
		
		if(!excel2JavaRowPathDir.isDirectory())
			excel2JavaRowPathDir.mkdirs();
		
		for(File file: excel2JavaRowPathDir.listFiles()) 
			if(file.isFile())
				file.delete();
		
		File eexcel2JavaExtPathDir = new File(excel2JavaExtPath);
		
		if(!eexcel2JavaExtPathDir.isDirectory())
			eexcel2JavaExtPathDir.mkdirs();
		
		File excel2JavaRowExtPathDir = new File(excel2JavaRowExtPath);
		
		if(!excel2JavaRowExtPathDir.isDirectory())
			excel2JavaRowExtPathDir.mkdirs();
		
		List<String> fileNames = new LinkedList<String>();
		for(File file: exceldir.listFiles()) {
			
			if(file.getName().endsWith(".xlsx") && file.isFile()) {
				
				if(file.getName().startsWith("global"))
					continue;
				
				if(file.getName().startsWith("~"))
					continue;
				
				String javaName = generateFile(file, excel2JavaPath, excel2JavaExtPath, excel2JavaRowPath, excel2JavaRowExtPath);
				fileNames.add(javaName);
			}
					
		}
		
		String excelFileRegisterPath = path + "\\src\\main\\java\\com\\snake\\excel2java\\excel\\register\\";
		File excelFileRegisterPathDir = new File(excelFileRegisterPath);
		
		if(!excelFileRegisterPathDir.isDirectory())
			excelFileRegisterPathDir.mkdirs();
		
		
		generateExcelRegisterFile(fileNames, excelFileRegisterPath);
	}
	
	public static final String FILE_NAME_SEP = "_";
	public static final String FILE_NAME_POINT = ".";
	
	public String generateFile(File file, String excel2JavaPath, String excel2JavaExtPath, 
			String excel2JavaRowPath, String excel2JavaRowExtPath) {
		
		String[][] format = readExcel(file);
		
		String fileName = file.getName();
		
		int index = fileName.indexOf(FILE_NAME_SEP);
		
		if(-1 == index)
			index = fileName.indexOf(FILE_NAME_POINT);
		
		String javaName = fileName.substring(0, index);
		javaName = javaName.substring(0, 1).toUpperCase() + javaName.substring(1);
		
		StringBuilder desc = new StringBuilder();
		desc.append(javaName)
			.append("=");
		
		StringBuilder propNames = new StringBuilder();
		
		propNames.append("\tpublic static final String[] PROP_NAMES = {");
		
		for(int i = 0; i < format.length; ++i) {
			
			String[] line = format[i];
			
			if(null == line[0])
				continue;
			
			if(0 != i)
				desc.append("#");
			
			for(int j = 0; j < line.length; ++j) {
				desc.append(line[j]);
				
				if(j != line.length - 1)
					desc.append(",");
			}
			
			if(0 != i)
				propNames.append(", ");
			
			propNames.append("\"")
					 .append(line[1])
					 .append("\"");
			
			
			
		}
		
		propNames.append("};");
		
		String descStr = desc.toString();
		log.info("\r\n\r\n------------- file desc: {}", descStr);
		
		generateExcelJavaFile(fileName, javaName, descStr, propNames.toString(), excel2JavaPath);
		generateExcelJavaExtFile(javaName, excel2JavaExtPath);
		generateExcelJavaRowFile(javaName, excel2JavaRowPath, format, descStr);
		generateExcelJavaRowExtFile(javaName, excel2JavaRowExtPath);
	
		return javaName;
	}
	
	public String[][] readExcel(File file) {
		
		log.info("\r\n\r\n\r\n------------- read {} --------------\r\n", file.getName());
		
		XSSFWorkbook workbook2007 = null;
		
		String[][] result = null;
		int i = 0;
		try {
			
			workbook2007 = new XSSFWorkbook(file);
			XSSFSheet sheet = workbook2007.getSheetAt(0);
			
			XSSFRow row0 = sheet.getRow(3);//类型
			XSSFRow row1 = sheet.getRow(4);//字段名
			XSSFRow row2 = sheet.getRow(1);//描述
			int length = row0.getLastCellNum();
			result = new String[length][3];
			String cellStr0="";
			String cellStr1="";
			String cellStr2="";
			for( ; i < length; ++i) {
				
				cellStr0=readString(row0, i);
				cellStr1=readString(row1, i);
				cellStr2=readString(row2, i);
				
				if(null == cellStr0 || null == cellStr1 || null == cellStr2){
					
					result[i][0] = null;
					result[i][1] = null;
					result[i][2] = null;
					continue;
				}
				
				if(cellStr0.isEmpty() || cellStr1.isEmpty() || cellStr2.isEmpty())
				{
					result[i][0] = null;
					result[i][1] = null;
					result[i][2] = null;
					continue;
				}
				
				result[i][0] = cellStr0;
				result[i][1] = cellStr1;
				result[i][2] = cellStr2;
				
				log.info("------------- reads column {}, {}, {}, {}", i, result[i][0], result[i][1], result[i][2]);
			}
			
			workbook2007.close();
			
		} catch (Exception e) {
			
			log.error("\r\n\r\n------------- read error in column {} --------------\r\n\r\n", i);
			log.error(e.toString());
			
			e.printStackTrace();
			
		} finally {
			
		}
		
		
		return result;
	}
	
	protected String readString(XSSFRow row, int index) {
		
		XSSFCell cell = row.getCell(index);
		
		return cell.getStringCellValue().trim();
	}


	public void generateExcelJavaFile(String fileName, String javaName, String descStr, String propNames, String excel2JavaPath) {
		
		StringBuilder builder = new StringBuilder();
		
		builder.append("package com.snake.excel2java.excel.common.file;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.ExcelLoader;\r\n")
			   .append("import com.snake.excel2java.excel.common.ext.row.")
			   .append(javaName)
			   .append("RowExt;\r\n\r\n");
		
		builder.append("public abstract class ")
			   .append(javaName)
			   .append(" extends ExcelLoader<")
			   .append(javaName)
			   .append("RowExt> {\r\n\r\n");
		
		builder.append("\t@Override\r\n")
		   .append("\tpublic String getJavaName() {\r\n")
		   .append("\t\treturn \"")
		   .append(javaName)
		   .append("\";\r\n")
		   .append("\t}\r\n\r\n");
		
		builder.append("\t@Override\r\n")
			   .append("\tpublic String getFileName() {\r\n")
			   .append("\t\treturn \"")
			   .append(fileName)
			   .append("\";\r\n")
			   .append("\t}\r\n\r\n");
		
		builder.append("\t@Override\r\n")
		   .append("\tpublic String getDesc() {\r\n")
		   .append("\t\treturn \"")
		   .append(descStr)
		   .append("\";\r\n")
		   .append("\t}\r\n\r\n");
		
		builder.append(propNames)
			   .append("\r\n\r\n");
		
		builder.append("\tpublic String[] getPropNames() {\r\n")
				.append("\t\treturn PROP_NAMES; \r\n")
				.append("\t}\r\n\r\n\r\n");
		
		
		builder.append("}");
		
		File javaFile = new File(excel2JavaPath + javaName + ".java");
		
		try {
			FileOutputStream fout = new FileOutputStream(javaFile);
			
			fout.write(builder.toString().getBytes("UTF-8"));
			fout.close();
			
			log.info("\r\n\r\n--------------------- write file {} success ---------------------", javaFile.getAbsolutePath());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			
			log.error("--------------------- write file error: {} ---------------------", e.getMessage());

			e.printStackTrace();
		}
		
	}

	public void generateExcelJavaExtFile(String javaName, String excel2JavaExtPath) {
		
		File javaFile = new File(excel2JavaExtPath + javaName + "Ext.java");
		
		if(javaFile.exists())
			return;
		
		StringBuilder builder = new StringBuilder();
		
		builder.append("package com.snake.excel2java.excel.common.ext.file;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.common.file.")
			   .append(javaName)
			   .append(";\r\n\r\n")
				.append("import lombok.NoArgsConstructor;\r\n")
				.append("import org.springframework.stereotype.Component;\r\n\r\n");
		
		builder.append("@Component\r\n")
				.append("@NoArgsConstructor\r\n")
				.append("public class ")
			   .append(javaName)
			   .append("Ext extends ")
			   .append(javaName)
			   .append(" {\r\n\r\n");
		
		builder.append("\t// 添加其他的功能\r\n")
			   .append("\tprotected void parse() {\r\n\r\n")
			   .append("\t}\r\n\r\n");
			   
		
		builder.append("}");
		
		
		try {
			FileOutputStream fout = new FileOutputStream(javaFile);
			
			fout.write(builder.toString().getBytes("UTF-8"));
			fout.close();
			
			log.info("\r\n\r\n--------------------- write file {} success ---------------------", javaFile.getAbsolutePath());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			
			log.error("--------------------- write file error: {} ---------------------", e.getMessage());

			e.printStackTrace();
		}
		
	}


	public void generateExcelJavaRowFile(String javaName, String excel2JavaRowPath, String[][] format, String descStr) {
			
		StringBuilder builder = new StringBuilder();
		
		builder.append("package com.snake.excel2java.excel.common.row;\r\n\r\n")
			   .append("import org.apache.poi.xssf.usermodel.XSSFRow;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.ExcelRowLoader;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.StringType;\r\n\r\n")
				.append("import lombok.Getter;\r\n")
				.append("import lombok.ToString;\r\n\r\n");
		
		builder.append("@Getter\r\n")
				.append("@ToString\r\n")
				.append("public abstract class ")
			   .append(javaName)
			   .append("Row extends ExcelRowLoader")
			   .append(" {\r\n\r\n");
		
		builder.append("\t//")
			   .append(descStr)
			   .append("\r\n\r\n");
		
//		StringBuilder funcBuilder = new StringBuilder();
		
		StringBuilder loadBuilder = new StringBuilder();
		loadBuilder.append("\tprotected void doLoad(XSSFRow row, int[] dataIndex) {\r\n\r\n")
				   .append("\t\tint colIndex = 0;\r\n\r\n");
		
//		StringBuilder toStringBuilder = new StringBuilder();
//		toStringBuilder.append("\tpublic String toString() {\r\n\r\n")
//					   .append("\t\tStringBuilder builder = new StringBuilder();\r\n\r\n")
//					   .append("\t\tbuilder.append(getClass().getName())\r\n")
//					   .append("\t\t\t\t.append(\" :\");\r\n\r\n");
		
		Set<String> nameSet = new HashSet<String>();
		for(int i = 0; i < format.length; ++i) {
			
			String[] line = format[i];
			
			if(null == line[0])
				continue;
						
			String type = line[0].toUpperCase();
			String name = line[1];
			String nameLower = name.substring(0, 1).toLowerCase() + name.substring(1);
			String nameUpper = name.substring(0, 1).toUpperCase() + name.substring(1);
			
			String nameLowerTemp = nameLower;
			int suffix = 1;
			boolean modifyName = false;
			while(nameSet.contains(nameLowerTemp)) {
				
				nameLowerTemp = nameLower + "_" + suffix++;
				modifyName = true;
			}
			
			if(modifyName) {
				suffix -= 1;
				nameLower += "_" + suffix;
				nameUpper += "_" + suffix;
			}
			
			nameSet.add(nameLower);
			
			if(name.equalsIgnoreCase("id")) {
				nameLower = "id";
				nameUpper = "Id";
			}
			
			builder.append("\tprotected ");
//			funcBuilder.append("\tpublic ");
			loadBuilder.append("\t\t")
					   .append(nameLower);
			
//			toStringBuilder.append("\t\tbuilder.append(\", ")
//						   .append(nameLower)
//						   .append("=\")\r\n")
//						   .append("\t\t\t\t.append(")
//						   .append(nameLower)
//						   .append(");")
//						   .append("\r\n\r\n");
			
			switch (type) {
			case "INT":
				builder.append("int ");
//				funcBuilder.append("int");
				loadBuilder.append(" = readInt(row, ");
				break;
			case "FLOAT":
				builder.append("float ");
//				funcBuilder.append("float");
				loadBuilder.append(" = readFloat(row, ");
				break;
			case "DOUBLE":
				builder.append("double ");
//				funcBuilder.append("double");
				loadBuilder.append(" = readDouble(row, ");
				break;
			case "BOOL":
				builder.append("boolean ");
//				funcBuilder.append("boolean");
				loadBuilder.append(" = readBool(row, ");
				break;
			default:
				builder.append("String ");
//				funcBuilder.append("String");
				loadBuilder.append(" = readString(row, ");
				break;
			}
			
			builder.append(nameLower)
				   .append(";\t\t//")
				   .append(line[2])
				   .append("\r\n");
			
//			funcBuilder.append(" get")
//					   .append(nameUpper)
//					   .append("() {\r\n")
//					   .append("\t\treturn ")
//					   .append(nameLower)
//					   .append(";\r\n")
//					   .append("\t}\r\n\r\n");
			
			loadBuilder.append("dataIndex[colIndex++]");
			
			switch (type) {
			case "INT":
			case "FLOAT":
			case "DOUBLE":
			case "BOOL":
				break;
			case "INT[]":
				loadBuilder.append(", StringType.INT_ARRAY");
				break;
			case "FLOAT[]":
				loadBuilder.append(", StringType.FLOA_TARRAY");
				break;
			case "DOUBLE[]":
				loadBuilder.append(", StringType.DOUBLE_ARRAY");
				break;
			case "BOOL[]":
				loadBuilder.append(", StringType.BOOL_ARRAY");
				break;
			case "STRING[]":
				loadBuilder.append(", StringType.STRING_ARRAY");
				break;
			default:
				loadBuilder.append(", StringType.STRING");
				break;
			}  
			loadBuilder.append(");\r\n");
			
		}
		
		loadBuilder.append("\t}\r\n\r\n");
		
//		toStringBuilder.append("\t\treturn builder.toString();\r\n\r\n")
//					   .append("\t}\r\n\r\n");
		
		builder.append("\r\n\r\n")
//			   .append(funcBuilder)
			   .append(loadBuilder);
//			   .append(toStringBuilder);
		
		
		builder.append("}");
		
		File javaFile = new File(excel2JavaRowPath + javaName + "Row.java");
		
		try {
			FileOutputStream fout = new FileOutputStream(javaFile);
			
			fout.write(builder.toString().getBytes("UTF-8"));
			fout.close();
			
			log.info("\r\n\r\n--------------------- write file {} success ---------------------", javaFile.getAbsolutePath());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			
			log.error("--------------------- write file error: {} ---------------------", e.getMessage());

			e.printStackTrace();
		}
		
	}

	public void generateExcelJavaRowExtFile(String javaName, String excel2JavaRowExtPath) {
		
		File javaFile = new File(excel2JavaRowExtPath + javaName + "RowExt.java");
		
		if(javaFile.exists())
			return;
		
		StringBuilder builder = new StringBuilder();
		
		builder.append("package com.snake.excel2java.excel.common.ext.row;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.common.row.")
			   .append(javaName)
			   .append("Row;\r\n\r\n");
		
		builder.append("public class ")
			   .append(javaName)
			   .append("RowExt extends ")
			   .append(javaName)
			   .append("Row {\r\n\r\n");
		
		builder.append("\t// 额外的解析, Ext文件中手动添加\r\n")
			   .append("\tprotected void parse() {\r\n\r\n")

			   .append("\t}\r\n\r\n");
		

		builder.append("}");
		
		
		try {
			FileOutputStream fout = new FileOutputStream(javaFile);
			
			fout.write(builder.toString().getBytes("UTF-8"));
			fout.close();
			
			log.info("\r\n\r\n--------------------- write file {} success ---------------------", javaFile.getAbsolutePath());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			
			log.error("--------------------- write file error: {} ---------------------", e.getMessage());

			e.printStackTrace();
		}
		
	}
	
	public void generateExcelRegisterFile(List<String> fileNames, String excelFileRegisterPath) {
		
		File file = new File(excelFileRegisterPath + "ExcelRegister.java");
		
		if(file.exists())
			file.delete();
		
		StringBuilder builder = new StringBuilder();
		
		builder.append("package com.snake.excel2java.excel.register;\r\n\r\n")
			   .append("import java.util.Map;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.IExcelLoader;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.common.ext.file.*;\r\n\r\n")
			   .append("import com.snake.excel2java.excel.global.ext.GlobalExt;\r\n\r\n")
				.append("import org.springframework.stereotype.Component;\r\n")
				.append("import org.springframework.beans.factory.annotation.Autowired;\r\n\r\n");
		
		builder.append("@Component\r\n")
				.append("public class ExcelRegister {\r\n\r\n");

		builder.append("\t@Autowired\r\n")
						.append("\tGlobalExt globalExt;\r\n");

		for(String fileName: fileNames) {

			String objName = fileName.substring(0, 1).toLowerCase() + fileName.substring(1);
			builder.append("\t@Autowired\r\n")
					.append("\t")
					.append(fileName)
					.append("Ext ")
					.append(objName)
					.append("Ext;\r\n");
		}
		builder.append("\r\n\r\n");


		builder.append("\tpublic void register(Map<String, IExcelLoader> loaderMap) {\r\n\r\n")
			   .append("\t\tloaderMap.put(globalExt.getFileName(), globalExt);\r\n");
		
		for(String fileName: fileNames) {

			String objName = fileName.substring(0, 1).toLowerCase() + fileName.substring(1);
			
			builder.append("\t\tloaderMap.put(")
				   .append(objName)
				   .append("Ext.getFileName(), ")
				   .append(objName)
				   .append("Ext);\r\n");
			
		}

		builder.append("\r\n\r\n\t}\r\n\r\n");
		

		builder.append("}");
		
		
		try {
			FileOutputStream fout = new FileOutputStream(file);
			
			fout.write(builder.toString().getBytes("UTF-8"));
			fout.close();
			
			log.info("\r\n\r\n--------------------- write file {} success ---------------------", file.getAbsolutePath());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			
			log.error("--------------------- write file error: {} ---------------------", e.getMessage());

			e.printStackTrace();
		}
		
	}
	

}
